package entities;

import interfaze.SpaceInvadersGamingPanel;

import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;

/** Movable Entities inheritance's main root. Inherit from Entity.
 * @author Team 1
 *
 */
@SuppressWarnings("serial")
public abstract class MovableEntity extends Entity {
	
	protected float dx, dy;				//The horizontal and vertical speed.
	protected boolean canMove;			//The flag that represents if the entity can move or not.
	protected long lastUpdate;
	public float lastX,lastY;
	public SpaceInvadersGamingPanel p;
	
	/**	MovableEntity's constructor, without initial movement.
	 * @param x : the entity's upper-left corner's x coordinate.
	 * @param y : the entity's upper-left corner's y coordinate.
	 */
	public MovableEntity(int x, int y) {
		super(x, y);
		this.dx = 0;
		this.dy = 0;
		canMove = false;
	}
	
	/**	MovableEntity's constructor, without initial movement.
	 * @param x : the entity's upper-left corner's x coordinate.
	 * @param y : the entity's upper-left corner's y coordinate.
	 * @param dx : the horizontal speed.
	 * @param dy : the vertical speed.
	 */
	public MovableEntity(int x, int y, int dx, int dy) {
		super(x, y);
		this.dx = dx;
		this.dy = dy;
		if(dx != 0 || dy != 0) {
			canMove = true;
		} else {
			canMove = false;
		}
	}
	
	/** Sets Entity's DX
	 * @param dx : x movement.
	 */
	public void setDX(float dx) {
		this.dx = dx;
		if(dx != 0) {
			canMove = true;
		} else if (dx == 0 && dy == 0){
			canMove = false;
		} else if(dx == 0 && dy != 0) {
			canMove = true;
		}
	}
	
	/** Sets Entity's DY
	 * @param dy : y movement.
	 */
	public void setDY(float dy) {
		this.dy = dy;
		if(dy != 0) {
			canMove = true;
		} else if(dy == 0 && dx == 0){
			canMove = false;
		} else if(dx == 0 && dy != 0) {
			canMove = true;
		}
	}
	
	/** Gets Entity's DX
	 * @return dx 
	 */
	public float getDX() {
		return dx;
	}
	
	/** Gets Entity's DY
	 * @return dy
	 */
	public float getDY() {
		return dy;
	}
	
	/** Returns if the entity can move or not.
	 * @return canMove, true if the entity can move, false otherwise.
	 */
	public boolean canMove() {
		return canMove;
	}
	
	/** Set if the entity can move or not, if it's set that the entity can't move
	 * the horizontal and vertical speed will be set to zero.
	 * @param canMove : the flag to set if the entity can move or not.
	 */
	public void setCanMove(boolean canMove) {
		if(!canMove) {
			this.canMove = false;
			dx = 0;
			dy = 0;
		} else {
			this.canMove = true;
		}
	}
	
	/** Updates Entity's location.
	 * Adds dx to the current value of the x coordinate of the entity.
	 * Adds dy to the current value of the y coordinate of the entity.
	 * Eventually it sets the position of the new x's and y's values.
	 */
	public void update() {
		x += dx;
		y += dy;
		this.setLocation(Math.round(x), Math.round(y));
	}
	
	/**Updates the Entity's location.
	 * moves the entity according to the time elapsed since the last update and the dx and dy attributes, which represent speed.
	 * @return	returns a boolean value telling whether the entity is getting out of the window's boundaries or not
	 */
	public boolean update2(){
		float timeElapsed=(System.currentTimeMillis()-lastUpdate)/1000.0F;
		if(this.isVisible()){
		if(timeElapsed > 0.01F){
			lastX=x;
			lastY=y;
			x+=dx*timeElapsed;
			y+=dy*timeElapsed;
			this.setLocation((int)x,(int)y);
			lastUpdate=System.currentTimeMillis();
			/*if(runningOffBounds()){
				bounceOff();
			}*/
		}
		}
	return runningOffBounds();
	
	}
	
	/**
	 * makes the MovableEntity change its horizontal speed, it's usually called when update2 returns the "true" value
	 */
	public void bounceOff() {
		if (this.getX() < 0){
			this.setDX(Math.abs(this.getDX()));
			setX((int)lastX);
			setY((int)lastY);
		}
		else if (this.getX() > p.window.getWidth() - this.getWidth()){
			this.setDX(-Math.abs(this.getDX()));
			setX((int)lastX);
			setY((int)lastY);
		}
	}
	
	/**
	 * @return returns whether the condition of "is the entity exiting the window's boundaries?" is true or not
	 */
	public boolean runningOffBounds() {
		return (x < 0 || y < 0
				|| x > p.window.getWidth() - this.getWidth()|| y > p
				.window.getHeight() - this.getHeight());
	}
	
	/** Return If the current entity collapses with the Entity entered as parameter. 
	 * @param other : The other entity.
	 * @return intersect[4] : a boolean array that returns true if entity intersects from: 0, bottom; 1, up; 2, left; 3 right.   
	 */
	public boolean[] intersects(Entity other) {
		boolean[] intersect = new boolean[4];
		Arrays.fill(intersect, Boolean.FALSE);
		Rectangle2D rectangle = new Rectangle((int)this.x, (int)this.y, this.width, this.height);
		Line2D.Float bottomLine = new Line2D.Float(other.x, other.y + other.height, other.x + other.width, other.y + other.height);
		Line2D.Float topLine = new Line2D.Float(other.x, other.y, other.x + other.width, other.y);
		Line2D.Float leftLine = new Line2D.Float(other.x, other.y, other.x, other.y + other.height);
		Line2D.Float rightLine = new Line2D.Float(other.x + other.width, other.y, other.x + other.width, other.y + other.height);
		if(bottomLine.intersects(rectangle)) {
			intersect[0] = true;
		}
		if(topLine.intersects(rectangle)) {
			intersect[1] = true;
		}
		if(leftLine.intersects(rectangle)) {
			intersect[2] = true;
		}
		if(rightLine.intersects(rectangle)) {
			intersect[3] = true;
		}
		return intersect;
	}
}
